///////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////

package ZBusBuffer;

////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////

// export ZBusIFC(..);
// export mkZBusBuffer;
// export BState;
// export resolveBusBState;

////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////

import List::*;
import ZBusUtil::*;

////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////

interface ZBusIFC #(type t) ;
   method Action      toBusSample(t value, Bool enable);
   method Action      fromBusSample(ZBit#(t) value, Bool isValid);
   method ZBit#(t)    toBusValue();
   method Bool        toBusDriven();
   method t           fromBusValue();
   method Bool        fromBusValid();
endinterface: ZBusIFC

////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////

module mkZBusBuffer (ZBusIFC #(t))
   provisos (Eq#(t), Bits#(t,st), Literal#(t));

   RWire#(t) value_reg();
   mkRWire reg_0(value_reg);

   Reg#(t) bus_value_reg();
   mkRegU reg_1(bus_value_reg);

   RWire#(Bool) enable_reg();
   mkRWire reg_2(enable_reg);

   Reg#(Bool) valid_reg();
   mkRegU reg_3(valid_reg);

   ConvertToZ#(t)       x();
   mkConvertToZ         x_inst(x);

   ConvertFromZ#(t)     y();
   mkConvertFromZ       y_inst(y);

   method toBusSample(value, enable);
      action
	 value_reg.wset(value);
	 enable_reg.wset(enable);
      endaction
   endmethod

   method fromBusSample(value, isValid);
      action
	 if (isValid)
	    bus_value_reg <= y.convert(value);
	 else
	    bus_value_reg <= 0;
	 valid_reg <= isValid;
      endaction
   endmethod

   method toBusValue();
      return x.convert(unJust(value_reg.wget()), unJust(enable_reg.wget()));
   endmethod

   method toBusDriven();
      return unJust(enable_reg.wget());
   endmethod

   method fromBusValid();
      return valid_reg;
   endmethod

   method fromBusValue();
      return bus_value_reg;
   endmethod

endmodule

////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////

typedef enum {Driven, HiZ, Invalid} BState
              deriving (Eq, Bits);

////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////

function BState resolveBusBState (List#(ZBusIFC #(t)) ifc_list);

   if (length(ifc_list) == 0)
      return HiZ;
   else
      return resolveBState(getBStateFromZBusIFC(head(ifc_list)),
			   resolveBusBState(tail(ifc_list)));
endfunction

////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////

function BState getBStateFromZBusIFC (ZBusIFC#(t) ifc);

   return createBState(ifc.toBusDriven);
endfunction

////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////

function BState createBState (Bool driven);
   if (driven)
      return Driven;
   else
      return HiZ;
endfunction

////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////

function BState resolveBState (BState bs_0, BState bs_1);
   if ((bs_0 == Invalid) || (bs_1 == Invalid) || ((bs_0 == Driven) && (bs_1 == Driven)))
      return Invalid;
   else if ((bs_0 == Driven) || (bs_1 == Driven))
      return Driven;
   else
      return HiZ;
endfunction

////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////

endpackage

////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////


